package cn.migu.music.hangupsms.service;

import java.security.MessageDigest;
import java.text.ParseException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;

import cn.migu.music.cljz.common.dataobject.BaseRsp;
import cn.migu.music.common.exception.BusinessException;
import cn.migu.music.common.exception.SystemException;
import cn.migu.music.common.redis.IRedisClient;
import cn.migu.music.common.utils.DateTimeConvertor;
import cn.migu.music.common.utils.LogUtils;
import cn.migu.music.hangupsms.cachemanage.handler.BusinessDbDataCacheServices;
import cn.migu.music.hangupsms.common.config.HangUpSmsConfig;
import cn.migu.music.hangupsms.common.config.HangUpSmsDbConfig;
import cn.migu.music.hangupsms.common.entity.BillInfo;
import cn.migu.music.hangupsms.common.entity.MaxNumInfo;
import cn.migu.music.hangupsms.common.entity.MsgSendInfo;
import cn.migu.music.hangupsms.common.entity.NotSendInfoObj;
import cn.migu.music.hangupsms.common.entity.QueryContractRightsReq;
import cn.migu.music.hangupsms.common.entity.QueryContractRightsRsp;
import cn.migu.music.hangupsms.common.entity.RightsInfo;
import cn.migu.music.hangupsms.common.entity.SendInfoObj;
import cn.migu.music.hangupsms.common.entity.TimeLimit;
import cn.migu.music.hangupsms.common.entity.TimeLimitInfo;
import cn.migu.music.hangupsms.common.entity.ToneCheckInfo;
import cn.migu.music.hangupsms.common.entity.UserSendMsgInfo;
import cn.migu.music.hangupsms.common.entity.VrbtSendMsg;
import cn.migu.music.hangupsms.common.util.LocalDateTimeUtils;
import cn.migu.music.hangupsms.dbcache.SentSmsCacheManager;
import cn.migu.music.hangupsms.model.LimitSendParam;
import cn.migu.music.hangupsms.remote.EgateWayMusicService;
import cn.migu.music.hangupsms.remote.IsRbtUserFeignService;
import cn.migu.music.hangupsms.remote.PhoneCodeService;
import cn.migu.music.hangupsms.remote.SMSFeignService;
import cn.migu.music.hangupsms.remote.VRBTUserServiceInfoService;
import cn.migu.music.hangupsms.repository.HangupMsgSendUserMapper;
import cn.migu.music.hangupsms.utils.ThreadContext;
import cn.migu.phonecode.api.domain.PhoneCode;
import lombok.extern.slf4j.Slf4j;

/**
 * @version 1.0 created by duanhao@migu.cn on 2022/3/8 5:05 下午
 */
@Component
@Slf4j
public class BaseService {
    public static final String          YYYY_MM                          = "yyyyMM";
    public static final String          YYYY_M_MDD                       = "yyyyMMdd";
    public static final String          DEFAULT                          = "default";
    public static final String          MSGCOUNT                         = "MSGCOUNT_";
    public static final String          COUNTLIMIT                       = "COUNTLIMIT";
    public static final String          ACCESS_PLATFORM_ID               = "014000E";
    public static final String          SECOND_FORMAT                    = "yyyyMMddHHmmss";
    /**
     * 权益类型
     */
    public static final String          RIGHTS_FIELD_CONTENT_TYPE        = "contentType";
    /**
     * redis加锁value值
     */
    public static final String          REDIS_LOCK_VALUE                 = "1";
    /**
     * 话单中，手机号位置
     */
    private static final int            CALL_BILL_CALLER_PHONE_INDEX     = 0;
    /**
     * 话单中，手机号位置
     */
    private static final int            CALL_BILL_PHONE_INDEX            = 1;
    /**
     * 话单中，铃音ID位置
     */
    private static final int            CALL_BILL_TONE_ID_INDEX          = 3;
    /**
     * 话单中，结束时间位置
     */
    private static final int            CALL_BILL_END_TIME_INDEX         = 5;
    /**
     * 铃音类型
     */
    private static final int            CALL_BILL_TYPE_INDEX             = 11;
    /**
     * 铃音类型
     */
    private static final int            CALL_BILL_TYPE_INDEX_NEW         = 10;
    private static final String         CALL_BILL_LIST_KAFKA_SWITCH_OPEN = "1";
    /**
     * 用户类型： 0 手机号
     */
    private static final String         USER_TYPE_MSISDN                 = "0";
    @Autowired
    private BusinessDbDataCacheServices businessDbDataCacheServices;
    @Autowired
    private HangUpSmsConfig             hangUpSmsConfig;
    @Autowired
    private HangUpSmsDbConfig           hangUpSmsDbConfig;
    @Autowired
    private PhoneCodeService            phoneCodeService;
    @Autowired
    private EgateWayMusicService        egateWayMusicService;
    @Autowired
    private VRBTUserServiceInfoService  vrbtUserServiceInfoService;
    @Autowired
    private IRedisClient                redisClient;
    @Autowired
    private HangupMsgSendUserMapper     hangupMsgSendUserMapper;
    @Autowired
    private IsRbtUserFeignService       isRbtUserFeignService;
    @Autowired
    private SentSmsCacheManager         sentSmsCacheManager;
    @Autowired
    private SMSFeignService             smsFeignService;
    @Autowired
    private CeLueShiService             ceLueShiService;

    public BaseRsp sendMsg(MsgSendInfo userSendMsgInfo, String msgtemplateId) {
        BaseRsp baseRsp = new BaseRsp();
        if (canLimit(userSendMsgInfo)) {
            baseRsp.setCode("200000");
            return baseRsp;
        }
        if (userSendMsgInfo.getProvinceId().matches(hangUpSmsConfig.getNoSendSmsProvinceId())) {
            LogUtils.info(log, "短信发送配置生效，手机号：%s 不发送短信", userSendMsgInfo.getPhonenumber());
            baseRsp.setCode("200000");
            return baseRsp;
        }
        String msg = userSendMsgInfo.getMsg();
        CeLueShiService.SmsSendResult smsSendResult = ceLueShiService.consumedByCeLueShi(msg, userSendMsgInfo.getProvinceId(),
            userSendMsgInfo.getPhonenumber(), msgtemplateId, computeSmsEndTime());
        if (smsSendResult.getConsumed()) {
            return smsSendResult.getBaseRsp();
        }
        msg = smsSendResult.getMsg();
        VrbtSendMsg event = new VrbtSendMsg();
        event.setMsisdn(userSendMsgInfo.getPhonenumber());
        event.setMsgPort(hangUpSmsConfig.getMsgSendPort());
        event.setMsgContent(msg);
        event.setAccessPlatformID(ACCESS_PLATFORM_ID);
        event.setDid(hangUpSmsConfig.getSendDid());
        event.setSeq(generateSeq(hangUpSmsConfig.getSendDid()));
        event.setDidpwd(generateMD5String(event.getSeq() + hangUpSmsConfig.getSendDidPwd()));
        event.setMsgtemplateId(msgtemplateId);
        event.setProvinceId(userSendMsgInfo.getProvinceId());
        return smsFeignService.vRBTSendMsgV3(event);
    }

    private Long computeSmsEndTime() {
        TimeLimit timeLimit = ThreadContext.get(ThreadContext.TIME_LIMIT, TimeLimit.class);
        String hangUpTime = ThreadContext.get(ThreadContext.HANG_UP_TIME, String.class);
        Date hangUpDateTime = DateTimeConvertor.parse(hangUpTime, SECOND_FORMAT);
        long delayTime = hangUpDateTime.getTime() + timeLimit.getLimitNum() * 60 * 1000L;
        String dateStr = DateTimeConvertor.formatDate(new Date());
        String endTimeStr = dateStr + " " + timeLimit.getEndTimeOfDay();
        Date endDateTime = DateTimeConvertor.parseDateTime(endTimeStr);
        long endTime = endDateTime.getTime();
        return Math.min(delayTime, endTime) / 1000L;
    }

    private boolean canLimit(MsgSendInfo userSendMsgInfo) {
        String provinceId = userSendMsgInfo.getProvinceId();
        int limitNum = Integer.parseInt(hangUpSmsConfig.getLimitCountMap().get(provinceId)); // 获取注解每秒加入桶中的token
        LocalDateTime now = LocalDateTime.now();
        String redisKey = "RATELIMITER_" + now.format(DateTimeFormatter.ofPattern(SECOND_FORMAT)) + "_" + provinceId;
        // 当多个节点操作同一个key的时候，先增加计数。避免下面的情况发生
        // 假设有A、B、C节点，如果我们限流50，而当前redis计数是49.
        // A、B、C三个节点如果同时取出数据（49），三个节点判断（49 < 50），那么实际达到的QPS会是52
        long currentCount = redisClient.incr(redisKey);
        if (currentCount == 1) { // 只有一个节点会满足该条件
            redisClient.expire(redisKey, hangUpSmsConfig.getCheckPhoneNumInterval());
        }

        try {
            if (currentCount <= limitNum) {
                // 执行方法
                if (hangUpSmsConfig.isNeedLog()) {
                    LogUtils.info(log, "放行流量,当前时间%s,%s", now.format(DateTimeFormatter.ofPattern(SECOND_FORMAT)), provinceId);
                }
                return false;
            } else {
                // 拒绝了请求（服务降级）
                redisCount(userSendMsgInfo.getProvinceName(), "CURRENTLIMIT");
                if (hangUpSmsConfig.isNeedLog()) {
                    LogUtils.info(log, "限流了,当前时间%s,%s", now.format(DateTimeFormatter.ofPattern(SECOND_FORMAT)), provinceId);
                }
                return true;
            }
        } catch (Exception throwable) {
            LogUtils.info(throwable, log, "限流器异常");
        }
        return false;
    }

    public static String generateSeq(String did) {
        StringBuilder sb = new StringBuilder();
        sb.append(did);
        sb.append(formatShortDateTime(new Date()));
        sb.append(RandomStringUtils.randomNumeric(8));
        return sb.toString();
    }

    /**
     * 格式日期时间字符串（短格式：yyyyMMddHHmmss）
     *
     * @param date
     *            日期时间
     * @return 日期时间字符串
     */
    private static String formatShortDateTime(Date date) {
        return DateTimeConvertor.format(date, SECOND_FORMAT);
    }

    /**
     * 对string进行MD5加密(标准方法)
     *
     * @param string
     *            String
     * @return String
     */
    public static String generateMD5String(String string) {
        String result = "";
        try {
            MessageDigest m = MessageDigest.getInstance("MD5");
            m.update(string.getBytes("UTF8"));
            byte[] s = m.digest();
            for (int i = 0; i < s.length; i++) {
                result += Integer.toHexString((0x000000ff & s[i]) | 0xffffff00).substring(6);
            }
        } catch (Exception e) {
            LogUtils.info(e, log, "加密失败");
            result = null;
        }
        return result;
    }

    /**
     * 记录rediskey值
     *
     * @param provinceName
     * @param keyName
     */
    public void redisCount(String provinceName, String keyName) {
        try {
            if (hangUpSmsConfig.isNeedCountLog()) {
                LocalDateTime now = LocalDateTime.now();
                Long dayCount = redisClient
                    .incr(MSGCOUNT + provinceName + "_" + keyName + "_" + now.format(DateTimeFormatter.ofPattern(YYYY_M_MDD)));
                if (dayCount == 1) {
                    redisClient.expire(
                        MSGCOUNT +
                                       provinceName +
                                       "_" +
                                       keyName +
                                       "_" +
                                       now.format(DateTimeFormatter.ofPattern(YYYY_M_MDD)),
                        60 * 60 * 24 * 40);
                }
            }
        } catch (Exception e) {
            LogUtils.info(e, log, "redis计数失败,provinceName:%s,keyName:%s", provinceName, keyName);
        }
    }

    public long queryRedisCountByName(String provinceName, String keyName, String date) {
        String count = redisClient.get(MSGCOUNT + provinceName + "_" + keyName + "_" + date);
        if (StringUtils.isNotEmpty(count)) {
            Long dayCount = Long.valueOf(count);
            return dayCount;
        } else {
            return 0L;
        }

    }

    public boolean contentPreProcess(BillInfo billInfo) {
        try {
            long startTime = System.currentTimeMillis();
            // 主叫|被叫|plus铃音编号|铃音id|开始时间|结束时间
            // 13801396200|13911912008||600926000001209039|20201217092552|20201217092600
            // 新增省份字段：beijing|13801396200|13911912008||600926000001209039|20201217092552|20201217092600
            if (!billInfo.isCanSend()) {
                return false;
            }
            redisCount(billInfo.getProvinceName(), "SUMCOUNT");
            // 校验第11位铃音播放内容
            if (checkToneContent(billInfo)) {
                return false;
            }
            String calleredProvinceId = getProvinceId(billInfo.getCalleredPhone(), billInfo.getProvinceName());
            // 分省校验是否需要发送短信
            if (checkProvince(billInfo.getCallerPhone(), calleredProvinceId, billInfo.getProvinceName())) {
                return false;
            }
            // 校验短信发送时间是否超过N分钟
            if (checkStartTime(billInfo.getEndTime(), calleredProvinceId, billInfo.getProvinceName())) {
                return false;
            }
            // 校验是否在黑名单中
            if (checkBlackUser(billInfo.getCallerPhone(), billInfo.getProvinceName())) {
                return false;
            }
            // 铃音id过滤是否需要发送短信
            if (checkContent(billInfo.getToneId(), billInfo.getProvinceName())) {
                return false;
            }
            if (!calleredProvinceId.matches(hangUpSmsConfig.getNoSendSmsProvinceId())) {
                long finish = System.currentTimeMillis();
                long timeElapsed = finish - startTime;
                LogUtils.info(log, "预处理挂机话单耗时：%s", timeElapsed);
            }
        } catch (Exception businessException) {
            LogUtils.info(businessException, log, "不满足发送短信的条件:%s", JSON.toJSONString(billInfo));
            return false;
        }
        return true;
    }

    @Async("hangupSmsAsyncExecutor")
    @SuppressWarnings("all")
    public void contentProcess(BillInfo billInfo, boolean checkLimit) {
        String calleredProvinceId = null;
        try {
            // 企业视频彩铃用户不发送短信
            calleredProvinceId = getProvinceId(billInfo.getCalleredPhone(), billInfo.getProvinceName());
            if (checkCorpStatus(billInfo, calleredProvinceId)) {
                return;
            }

        } catch (Exception businessException) {
            LogUtils.info(businessException, log, "不满足发送短信的条件");
            return;
        }
        TimeLimit timeLimit = getTimeLimit(calleredProvinceId);
        ThreadContext.put(ThreadContext.TIME_LIMIT, timeLimit);
        ThreadContext.put(ThreadContext.HANG_UP_TIME, billInfo.getEndTime());
        // 校验是否超过发送次数
        // 这里要加锁
        boolean expire = redisClient.setnxExpire(billInfo.getCallerPhone(), REDIS_LOCK_VALUE, hangUpSmsConfig.getCheckPhoneNumInterval());
        if (!expire) {
            return;
        }
        try {
            LocalDateTime now = LocalDateTime.now();
            MaxNumInfo maxNumInfo = getMaxNumInfo(calleredProvinceId);
            // 校验发送的次数是否超限
            boolean limitSendFromDb = false;
            boolean limitSendFromCache = false;
            boolean limitSend = false;
            List<LocalDateTime> timeList = new ArrayList<>();
            // 是否开启缓存功能
            int ret = 0;
            LimitSendParam param = new LimitSendParam(billInfo, calleredProvinceId, now, maxNumInfo, limitSendFromDb, limitSendFromCache,
                timeList, ret);
            limitSend = isLimitSend(param);
            if (checkLimit && limitSend) {
                redisClient.del(billInfo.getCallerPhone());
                return;
            }
            String msgTemplateId = getTemplateId(billInfo);
            if (StringUtils.isEmpty(msgTemplateId)) {
                return;
            }
            String msg = hangUpSmsDbConfig.getMsgTemplateMap().get(msgTemplateId);
            if (StringUtils.isEmpty(msg) || StringUtils.equals("null", msg)) {
                return;
            }
            MsgSendInfo userSendMsgInfo = new MsgSendInfo();
            userSendMsgInfo.setPhonenumber(billInfo.getCallerPhone());
            userSendMsgInfo.setMsg(msg);
            userSendMsgInfo.setProvinceId(calleredProvinceId);
            userSendMsgInfo.setProvinceName(billInfo.getProvinceName());
            BaseRsp baseRsp = sendMsg(userSendMsgInfo, msgTemplateId);
            if (StringUtils.equals("000000", baseRsp.getCode())) {
                updateRedis(calleredProvinceId, msgTemplateId, now);
                updateDb(billInfo.getCallerPhone(), msgTemplateId, now, calleredProvinceId);
                if (hangUpSmsConfig.isSendRecordCacheSwitch()) {
                    sentSmsCacheManager.saveSendRecord(billInfo.getCallerPhone(), now);
                }
            }
        } catch (Exception e) {
            LogUtils.error(e, log, "短信发送失败");
        } finally {
            redisClient.del(billInfo.getCallerPhone());
        }
    }

    private String getTemplateId(BillInfo billInfo) {
        // 通过主叫用户的功能开通情况来发动短信
        String userTypeSuffix = getUserTypeSuffix(billInfo.getCallerPhone());
        String userType = StringUtils.replace(userTypeSuffix, "_", "");
        // 尝试获取铃音短信
        String msgTemplateId = billInfo.getToneId() + userType;
        if (hangUpSmsDbConfig.getMsgTemplateMap().containsKey(msgTemplateId)) {
            return msgTemplateId;
        }
        // 尝试获取省份短信，注意，老逻辑里面有铃音短信+默认短信逻辑，目前是弃用状态
        msgTemplateId = queryMsg(billInfo.getCallerPhone(), billInfo.getToneId(), billInfo.getCallerProvinceId(), userTypeSuffix);
        if (hangUpSmsDbConfig.getMsgTemplateMap().containsKey(msgTemplateId)) {
            return msgTemplateId;
        }
        // 尝试获取全国默认短信
        String suffix = "";
        if (StringUtils.equals(userType, "00")) {
            suffix = "2";
        } else if (StringUtils.equals(userType, "10")) {
            suffix = "3";
        } else if (StringUtils.equals(userType, "11")) {
            suffix = "4";
        } else {
            return null;
        }
        msgTemplateId = DEFAULT + suffix;
        if (hangUpSmsDbConfig.getMsgTemplateMap().containsKey(msgTemplateId)) {
            return msgTemplateId;
        }
        return null;
    }

    private String getUserTypeSuffix(String phoneNumber) {
        String userStatus = getUserStatus(phoneNumber);
        if (StringUtils.equals("0", userStatus)) {
            return "0_0";
        }
        String baseVipStatus = getBaseVipStatus(phoneNumber);
        return userStatus + "_" + baseVipStatus;
    }

    private boolean isLimitSend(LimitSendParam param) {
        BillInfo billInfo = param.getBillInfo();
        String calleredProvinceId = param.getCalleredProvinceId();
        LocalDateTime now = param.getNow();
        MaxNumInfo maxNumInfo = param.getMaxNumInfo();
        boolean limitSendFromDb = param.isLimitSendFromDb();
        boolean limitSendFromCache = param.isLimitSendFromCache();
        List<LocalDateTime> timeList = param.getTimeList();
        int ret = param.getRet();

        boolean limitSend;
        if (hangUpSmsConfig.isSendRecordCacheSwitch()) {
            ret = sentSmsCacheManager.isSentLimit(billInfo.getCallerPhone(), now, maxNumInfo);
            limitSendFromCache = (ret == 1); // 缓存决定是否发送
            limitSend = limitSendFromCache;
            if (!hangUpSmsConfig.isSendRecordCacheSingle() && (hangUpSmsConfig.isSendRecordCacheWarmup() || ret == -1)) { // 同时使用缓存和数据库
                // 1.预热期始终以DB为准
                // 2.缓存无记录以DB为准：ret == -1
                limitSendFromDb = checkUserSendTime(billInfo.getCallerPhone(), now, maxNumInfo, calleredProvinceId, billInfo.getProvinceName(),
                    timeList);
                limitSend = limitSendFromDb;
            }
        } else { // 未开启缓存
            limitSendFromDb = checkUserSendTime(billInfo.getCallerPhone(), now, maxNumInfo, calleredProvinceId, billInfo.getProvinceName(), timeList);
            limitSend = limitSendFromDb;
        }
        if (hangUpSmsConfig.isSendRecordCacheSwitch() && CollectionUtils.isNotEmpty(timeList)) {
            sentSmsCacheManager.saveSendRecordList(billInfo.getCallerPhone(), timeList);
        }
        // 开启缓存并且同时使用缓存和数据库的时候开启对比统计
        if (hangUpSmsConfig.isSendRecordCacheSwitch() && !hangUpSmsConfig.isSendRecordCacheSingle()) {
            sentSmsCacheManager.staticCacheAndDbCompare(limitSendFromDb, limitSendFromCache, ret, billInfo.getCallerPhone(), timeList);
        }
        return limitSend;
    }

    public BillInfo getBillInfo(String content) {
        BillInfo billInfo = new BillInfo();
        String[] params = content.split("\\|");
        if (params.length < 5) {
            LogUtils.error(log, "话单-%s-参数错误", content);
            billInfo.setCanSend(false);
            return billInfo;
        }
        if (StringUtils.isNumeric(params[0])) {
            billInfo.setCallerPhone(params[CALL_BILL_CALLER_PHONE_INDEX]);
            billInfo.setCalleredPhone(params[CALL_BILL_PHONE_INDEX]);
            billInfo.setToneId(params[CALL_BILL_TONE_ID_INDEX]);
            billInfo.setEndTime(params[CALL_BILL_END_TIME_INDEX]);
        } else {
            billInfo.setProvinceName(params[0]);
            // beijing|13801396200|13911912008||600926000001209039|20201217092552|20201217092600
            if (params.length < 6) {
                LogUtils.error(log, "话单(省份字段)-%s-参数错误", content);
                billInfo.setCanSend(false);
                return billInfo;
            }
            billInfo.setCallerPhone(params[CALL_BILL_CALLER_PHONE_INDEX + 1]);
            billInfo.setCalleredPhone(params[CALL_BILL_PHONE_INDEX + 1]);
            billInfo.setToneId(params[CALL_BILL_TONE_ID_INDEX + 1]);
            billInfo.setEndTime(params[CALL_BILL_END_TIME_INDEX + 1]);
            if (params.length >= 12) {
                billInfo.setToneType(params[CALL_BILL_TYPE_INDEX]);
            }
        }
        if (billInfo.getCallerPhone().matches(hangUpSmsConfig.getWhiteMsisdns())) {
            LogUtils.info(log, "接收到kafka消息,话单内容:%s", content);
        }
        return billInfo;
    }

    private boolean checkToneContent(BillInfo billInfo) {
        String toneType = billInfo.getToneType();
        if (StringUtils.isNotEmpty(toneType) && !toneType.equals("1")) {
            redisCount(billInfo.getProvinceName(), "INDEX_12");
            if (hangUpSmsConfig.isNeedLog()) {
                LogUtils.info(log, "播放被叫铃音之外的铃音内容不发送短信");
            }
            return true;
        }
        return false;
    }

    /**
     * 获取被叫号码的省份id
     *
     * @param calledPhone
     * @param provinceName
     * @return
     */
    private String getProvinceId(String calledPhone, String provinceName) {
        String callerProvinceId = "";
        if (StringUtils.isNotEmpty(provinceName)) {
            callerProvinceId = hangUpSmsConfig.getProvinceNameAndIdMap().get(provinceName);
        }
        if (StringUtils.isEmpty(callerProvinceId)) {
            callerProvinceId = phoneCodeService.getProvinceId(calledPhone);
        }
        return callerProvinceId;
    }

    private boolean checkCorpStatus(BillInfo billInfo, String calleredProvinceId) {
        // 非移动号码不发送短信
        String callerPhone = billInfo.getCallerPhone();
        if (StringUtils.length(callerPhone) < 11) {
            return true;
        }
        // 查询是否是移动号段
        PhoneCode phoneCodeWithoutNp = phoneCodeService.getPhoneCodeWithoutNp(callerPhone);
        if (null == phoneCodeWithoutNp || !StringUtils.equals(phoneCodeWithoutNp.getProvinceId(), calleredProvinceId)) {
            redisCount(billInfo.getProvinceName(), "NOMOBILE");
            if (hangUpSmsConfig.isNeedLog()) {
                LogUtils.info(log, "非移动号段或不是同个省份不发送短信：%s", callerPhone);
            }
            return true;
        }
        billInfo.setCallerProvinceId(phoneCodeWithoutNp.getProvinceId());
        if (phoneCodeWithoutNp.getProvinceId().matches(hangUpSmsConfig.getNeedCheckCorpProvinceIds())) {
            // 这里需要查询主叫用户的localId 这里需要查询是否是音频彩铃用户
            boolean isCrbtStatus = isRbtUserFeignService.crbtStatus(callerPhone, "014000D");
            if (isCrbtStatus) {
                redisCount(billInfo.getProvinceName(), "CRBTUSER");
                if (hangUpSmsConfig.isNeedLog()) {
                    LogUtils.info(log, "音频彩铃用户不发短信：%s", callerPhone);
                }
                return true;
            }
        }
        return false;
    }

    private MaxNumInfo getMaxNumInfo(String callerProvinceId) {
        MaxNumInfo maxNumInfo = hangUpSmsDbConfig.getMaxNumInfoMap().get(callerProvinceId);
        if (null == maxNumInfo) {
            maxNumInfo = hangUpSmsDbConfig.getMaxNumInfoMap().get(DEFAULT);
        }
        return maxNumInfo;
    }

    private void updateDb(String calledPhone, String msgTemplateId, LocalDateTime now, String provinceId) {
        UserSendMsgInfo userSendMsgInfo = new UserSendMsgInfo();
        userSendMsgInfo.setPhonenumber(calledPhone);
        userSendMsgInfo.setSendmsgtime(now);
        if (StringUtils.length(msgTemplateId) == 20) {
            String shortId = hangUpSmsDbConfig.getMsgTemplateMap().get("t:" + msgTemplateId);
            shortId = StringUtils.isEmpty(shortId) ? "0000" : shortId;
            userSendMsgInfo.setMsgtemplateid(shortId);
        } else if (StringUtils.startsWith(msgTemplateId, DEFAULT)) {
            String shortId = StringUtils.substring(msgTemplateId, 7);
            userSendMsgInfo.setMsgtemplateid(shortId);
        } else {
            userSendMsgInfo.setMsgtemplateid(msgTemplateId);
        }
        userSendMsgInfo.setLocalid(Integer.valueOf(hangUpSmsConfig.getProvinceAndLocalIdMap().get(provinceId)));
        hangupMsgSendUserMapper.save(userSendMsgInfo);
    }

    private void updateRedis(String provinceId, String templateId, LocalDateTime now) {

        Long monthCount = redisClient.incr(MSGCOUNT + provinceId + "_" + templateId + "_" + now.format(DateTimeFormatter.ofPattern(YYYY_MM)));
        if (monthCount == 1) {
            redisClient.expire(MSGCOUNT + provinceId + "_" + templateId + "_" + now.format(DateTimeFormatter.ofPattern(YYYY_MM)),
                60 * 60 * 24 * 365);
        }
        Long dayCount = redisClient.incr(MSGCOUNT + provinceId + "_" + templateId + "_" + now.format(DateTimeFormatter.ofPattern(YYYY_M_MDD)));
        if (dayCount == 1) {
            redisClient.expire(MSGCOUNT + provinceId + "_" + templateId + "_" + now.format(DateTimeFormatter.ofPattern(YYYY_M_MDD)),
                60 * 60 * 24 * 60);
        }

    }

    /**
     * 获取短信内容
     *
     * @param callerPhone
     * @param toneId
     * @return
     */
    private String queryMsg(String callerPhone, String toneId, String callerProvinceId, String userType) {
        if (StringUtils.isEmpty(callerProvinceId)) {
            callerProvinceId = phoneCodeService.getProvinceId(callerPhone);
        }
        Map<String, Map<String, String>> msgContentMap = hangUpSmsConfig.getMsgContentMap();
        Map<String, String> provinceContentMap = msgContentMap.get(callerProvinceId);
        Map<String, String> defaultContentMap = msgContentMap.get(DEFAULT);
        // 检验是否配置了特殊的固定的铃音
        String content = getContentById(toneId, provinceContentMap, defaultContentMap);
        if (StringUtils.isNotEmpty(content)) {
            return content;
        }
        // 查询视频彩铃功能开通情况
        return getMsgByKey(provinceContentMap, defaultContentMap, userType);
    }

    private String getContentById(String toneId, Map<String, String> provinceContentMap, Map<String, String> defaultContentMap) {
        String provinceMsg = "";
        if (null != provinceContentMap) {
            provinceMsg = provinceContentMap.get(toneId);
        }
        String defaultMsg = defaultContentMap.get(toneId);
        return StringUtils.isNotEmpty(provinceMsg) ? provinceMsg : defaultMsg;
    }

    private String getMsgByKey(Map<String, String> provinceContentMap, Map<String, String> defaultContentMap, String key) {
        if (null != provinceContentMap && StringUtils.isNotEmpty(provinceContentMap.get(key))) {
            return provinceContentMap.get(key);
        } else {
            return defaultContentMap.get(key);
        }
    }

    public String getBaseVipStatus(String phoneNum) {
        List<String> serviceList = queryRbtMonthlyServiceIds(ACCESS_PLATFORM_ID, phoneNum, "127");
        if (CollectionUtils.isEmpty(serviceList)) {
            return "0";
        }
        boolean hasBaseVip = removeBaseVipService(serviceList);
        return hasBaseVip ? "1" : "0";
    }

    /**
     * 确认视频彩铃用户状态为正常
     *
     * @param phonenumber
     *            用户的手机号码；
     */
    public String getUserStatus(String phonenumber) {
        Map<String, Boolean> userIsOK = vrbtUserServiceInfoService.vRBTUserIsOK(phonenumber, ACCESS_PLATFORM_ID);
        return userIsOK.get(phonenumber) ? "1" : "0";
    }

    /**
     * 根据权益查询业务ID
     *
     * @param channel
     * @param msisdn
     * @param rightValue
     * @return
     */
    private List<String> queryRbtMonthlyServiceIds(String channel, String msisdn, String rightValue) {
        QueryContractRightsReq queryContractRightsReq = new QueryContractRightsReq();
        queryContractRightsReq.setAccessPlatformID(channel);
        queryContractRightsReq.setUserAccountType(USER_TYPE_MSISDN);
        queryContractRightsReq.setUserAccount(msisdn);
        List<RightsInfo> rightsInfoList = new ArrayList<>();
        RightsInfo rightsInfo = new RightsInfo();
        rightsInfo.setRightsField(RIGHTS_FIELD_CONTENT_TYPE);
        rightsInfo.setRightsValue(rightValue);
        rightsInfoList.add(rightsInfo);
        queryContractRightsReq.setRightsList(rightsInfoList);
        QueryContractRightsRsp queryContractRightsRsp = egateWayMusicService.queryContractRights(queryContractRightsReq);
        if (queryContractRightsRsp.getResult() == null || !Objects.equals(queryContractRightsRsp.getResult().getResultCode(), "000000")) {
            throw BusinessException.newInstance("查询用户视频彩铃业务失败");
        }
        return queryContractRightsRsp.getMatchServiceId();
    }

    /**
     * 移除视频彩铃基础会员包业务
     *
     * @param serviceList
     * @return
     */
    private boolean removeBaseVipService(List<String> serviceList) {
        return removeServiceId(serviceList, hangUpSmsConfig.getVrbtBaseVipServiceId());
    }

    private boolean removeServiceId(List<String> serviceList, String destService) {
        boolean hasDestService = false;
        if (StringUtils.isNotBlank(destService)) {
            String[] vrbtExperienceServiceArr = destService.split("\\|");
            for (String experienceService : vrbtExperienceServiceArr) {
                if (CollectionUtils.isNotEmpty(serviceList) && serviceList.remove(experienceService)) {
                    hasDestService = true;
                }
            }
        }
        return hasDestService;
    }

    private boolean checkUserSendTime(String callerPhone,
                                      LocalDateTime now,
                                      MaxNumInfo maxNumInfo,
                                      String provinceId,
                                      String provinceName,
                                      List<LocalDateTime> timeList) {
        String localId = hangUpSmsConfig.getProvinceAndLocalIdMap().get(provinceId);
        List<UserSendMsgInfo> userSendMsgInfos = hangupMsgSendUserMapper.querySendMsgUserInfo(callerPhone, Integer.valueOf(localId));
        for (UserSendMsgInfo userSendMsgInfo : userSendMsgInfos) {
            timeList.add(userSendMsgInfo.getSendmsgtime());
        }
        LocalDateTime startDayOfMonth = LocalDateTimeUtils.getStartOrEndDayOfMonth(now.toLocalDate(), true);
        LocalDateTime startDayOfWeek = LocalDateTimeUtils.getStartOrEndDayOfWeek(now.toLocalDate(), true);
        LocalDateTime startDayOfDay = LocalDateTimeUtils.getStartOrEndDayOfDay(now.toLocalDate(), true);
        int currentNumOfDay = 0;
        int currentNumOfWeek = 0;
        int currentNumOfMonth = 0;
        if (checkDayOfMonth(userSendMsgInfos, startDayOfMonth, currentNumOfMonth, maxNumInfo.getMaxMonthNum())) {
            redisCount(provinceName, COUNTLIMIT);
            return true;
        }
        if (checkDayOfMonth(userSendMsgInfos, startDayOfWeek, currentNumOfWeek, maxNumInfo.getMaxWeekNum())) {
            redisCount(provinceName, COUNTLIMIT);
            return true;
        }
        if (checkDayOfMonth(userSendMsgInfos, startDayOfDay, currentNumOfDay, maxNumInfo.getMaxDayNum())) {
            redisCount(provinceName, COUNTLIMIT);
            return true;
        }
        return false;
    }

    private boolean checkDayOfMonth(List<UserSendMsgInfo> userSendMsgInfos,
                                    LocalDateTime startDayOfMonth,
                                    int currentNumOfMonth,
                                    Integer maxMonthNum) {
        for (UserSendMsgInfo userSendMsgInfo : userSendMsgInfos) {
            LocalDateTime sendmsgtime = userSendMsgInfo.getSendmsgtime();
            if (sendmsgtime.isAfter(startDayOfMonth)) {
                currentNumOfMonth++;
                if (checkLimitNum(currentNumOfMonth, maxMonthNum)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean checkLimitNum(int currentNumOfMonth, Integer maxMonthNum) {
        if (currentNumOfMonth >= maxMonthNum) {
            if (hangUpSmsConfig.isNeedLog()) {
                LogUtils.info(log, "发送信息次数超过限制,currentNumOfMonth:%s,maxMonthNum:%s", currentNumOfMonth, maxMonthNum);
            }
            return true;
        }
        return false;
    }

    private boolean checkProvince(String calledPhone, String calleredProvinceId, String provinceName) {
        // 这里需要有白名单用户添加
        if (calledPhone.matches(hangUpSmsConfig.getWhiteMsisdns())) {
            return false;
        }
        if (calleredProvinceId.matches(hangUpSmsDbConfig.getCallerNoSendProvinceIds())) {
            redisCount(provinceName, "PROVINCE");
            if (hangUpSmsConfig.isNeedLog()) {
                LogUtils.info(log, "当前省份不做信息发送,provinceId:%s,phone:%s", calleredProvinceId, calledPhone);
            }
            return true;
        }
        return false;
    }

    private boolean checkContent(String toneId, String provinceName) {
        // G位为1或4不发
        if (toneId.substring(6, 7).matches(hangUpSmsConfig.getCallerNoSendGIndex())) {
            redisCount(provinceName, "INDEXG");
            printADNotSendLog(toneId);
            return true;
        }
        if (StringUtils.isNotEmpty(hangUpSmsConfig.getPrefixsToneid())) {
            String[] split = hangUpSmsConfig.getPrefixsToneid().split("\\|");
            for (int i = 0; i < split.length; i++) {
                if (toneId.startsWith(split[i])) {
                    if (hangUpSmsConfig.isNeedLog()) {
                        LogUtils.info(log, "优先音频铃音不下发短信,toneId:%s", toneId);
                    }
                    return true;
                }
            }
        }
        return false;
    }

    private void printADNotSendLog(String toneId) {
        if (hangUpSmsConfig.isNeedLog()) {
            LogUtils.info(log, "广告视频彩铃内容或者企业视频彩铃内容不下发短信,toneId:%s", toneId);
        }
    }

    private boolean checkBlackUser(String callerPhone, String provinceName) {
        if (businessDbDataCacheServices.isBlackNum(callerPhone)) {
            redisCount(provinceName, "BLACK");
            if (hangUpSmsConfig.isNeedLog()) {
                LogUtils.info(log, "黑名单用户: %s", callerPhone);
            }
            return true;
        }
        return false;
    }

    private boolean checkStartTime(String endTime, String provinceId, String provinceName) throws ParseException {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime end = LocalDateTime.parse(endTime, DateTimeFormatter.ofPattern(SECOND_FORMAT));

        TimeLimit timeLimit = getTimeLimit(provinceId);
        if (now.isAfter(end.plusMinutes(timeLimit.getLimitNum()))) {
            redisCount(provinceName, "OUTTIME");
            if (hangUpSmsConfig.isNeedLog()) {
                LogUtils.info(log, "超过话单超时时间，不再处理");
            }
            return true;
        }
        // 当前时间是否在该省可以发送短信的时间内
        try {
            // 当前时间是否在该省可以发送短信的时间内
            Date nowTime = DateTimeConvertor.parse(DateTimeConvertor.formatTime(new Date()), DateTimeConvertor.TIME_FORMAT);
            if (!timeCalendar(nowTime, DateTimeConvertor.parse(timeLimit.getStartTimeOfDay(), DateTimeConvertor.TIME_FORMAT),
                DateTimeConvertor.parse(timeLimit.getEndTimeOfDay(), DateTimeConvertor.TIME_FORMAT))) {
                if (hangUpSmsConfig.isNeedLog()) {
                    LogUtils.info(log, "不在省份允许发送短信的时间范围内，不再处理");
                }
                return true;
            }
        } catch (SystemException e) {
            LogUtils.info(e, log, "时间转换失败");
            return true;
        }
        return false;
    }

    private TimeLimit getTimeLimit(String provinceId) {
        Map<String, TimeLimit> provinceTimeLimitMap = hangUpSmsDbConfig.getProvinceTimeLimitMap();
        TimeLimit provincetimeLimit = provinceTimeLimitMap.get(provinceId);
        TimeLimit defaultTimeLimit = provinceTimeLimitMap.get(DEFAULT);
        if (provincetimeLimit == null) {
            return defaultTimeLimit;
        }

        provincetimeLimit.setStartTimeOfDay(StringUtils.defaultIfEmpty(provincetimeLimit.getStartTimeOfDay(), defaultTimeLimit.getStartTimeOfDay()));
        provincetimeLimit.setEndTimeOfDay(StringUtils.defaultIfEmpty(provincetimeLimit.getEndTimeOfDay(), defaultTimeLimit.getEndTimeOfDay()));
        provincetimeLimit.setLimitNum(provincetimeLimit.getLimitNum() != 0 ? provincetimeLimit.getLimitNum() : defaultTimeLimit.getLimitNum());
        return provincetimeLimit;
    }

    public List<TimeLimitInfo> getTimeLimitList() {
        List<TimeLimitInfo> timeLimitInfoList = new ArrayList<>();
        Map<String, TimeLimit> provinceTimeLimitMap = hangUpSmsDbConfig.getProvinceTimeLimitMap();
        TimeLimit defaultTimeLimit = provinceTimeLimitMap.get(DEFAULT);
        for (Map.Entry<String, TimeLimit> entry : provinceTimeLimitMap.entrySet()) {
            TimeLimitInfo timeLimitInfo = new TimeLimitInfo();
            if (StringUtils.equals(DEFAULT, entry.getKey())) {
                timeLimitInfo.setProvinceId(DEFAULT);
            } else {
                timeLimitInfo.setProvinceId(entry.getKey());
            }
            TimeLimit provincetimeLimit = entry.getValue();
            timeLimitInfo.setStartTimeOfDay(StringUtils.defaultIfEmpty(provincetimeLimit.getStartTimeOfDay(), defaultTimeLimit.getStartTimeOfDay()));
            timeLimitInfo.setEndTimeOfDay(StringUtils.defaultIfEmpty(provincetimeLimit.getEndTimeOfDay(), defaultTimeLimit.getEndTimeOfDay()));
            timeLimitInfo.setLimitNum(provincetimeLimit.getLimitNum() != 0 ? provincetimeLimit.getLimitNum() : defaultTimeLimit.getLimitNum());
            timeLimitInfoList.add(timeLimitInfo);
        }
        return timeLimitInfoList;
    }

    public SendInfoObj getSendInfoObj() {
        SendInfoObj sendInfoObj = new SendInfoObj();
        sendInfoObj.setToneType(hangUpSmsConfig.getToneType());
        sendInfoObj.setTimeLimit(getTimeLimitList());
        return sendInfoObj;
    }

    public NotSendInfoObj getNotSendInfoObj() {
        NotSendInfoObj notSendInfoObj = new NotSendInfoObj();
        notSendInfoObj.setCallerNoSendProvinceIds(hangUpSmsDbConfig.getCallerNoSendProvinceIds());
        notSendInfoObj.setPrefixsToneid(hangUpSmsConfig.getPrefixsToneid());
        ToneCheckInfo toneCheckInfo = new ToneCheckInfo();
        toneCheckInfo.setCheckPos(hangUpSmsConfig.getCheckPos());
        toneCheckInfo.setCheckValue(hangUpSmsConfig.getCallerNoSendGIndex());
        notSendInfoObj.setToneIdCheck(toneCheckInfo);
        return notSendInfoObj;
    }

    public static boolean timeCalendar(Date nowTime, Date dayTimeStart, Date dayTimeEnd) {
        // 设置当前时间
        Calendar date = Calendar.getInstance();
        date.setTime(nowTime);
        // 设置开始时间
        Calendar timeStart = Calendar.getInstance();
        timeStart.setTime(dayTimeStart);
        // 设置结束时间
        Calendar timeEnd = Calendar.getInstance();
        timeEnd.setTime(dayTimeEnd);
        return date.after(timeStart) && date.before(timeEnd);
    }

}
